"""
Copyright 2009, Thomas Dejanovic, Jay Shurtz.
 
This is free software; you can redistribute it and/or modify it
under the terms of the GNU Lesser General Public License as
published by the Free Software Foundation; either version 2.1 of
the License, or (at your option) any later version.

This software is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.

You should have received a copy of the GNU Lesser General Public
License along with this software; if not, write to the Free
Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
02110-1301 USA, or see the FSF site: http://www.fsf.org.
"""

id = "$Id: hatch_info_elaborator.py 667 2010-07-01 00:12:17Z jayshurtz $"
# $URL: http://hatch.googlecode.com/svn/tags/taggle_release_2.3/hatch/hatch_visitors/hatch_info_elaborator.py $
# $Author: jayshurtz $
version = " ".join(id.split()[1:3])


import to_hatch_structure
from hatch_visitor  import *
from hatch_constants import *   # Should comment on just about every property.


class HatchInfoElaborator(HatchVisitor):
    """ Class for generating info about a hatch node based on it's
        properties [and type.]

        This is done to flesh out the documentation by converting the
        coment property to a list of strings.

        This class implements all the documentation-related methods
        that would otherwise be needed by the hatch node classes
        themselves.

        Note: This class should visit AFTER the HDL elaborator, which
            populates the 'input_ports' & 'output_ports' properties.
            This class should visit AFTER the property checker, which
            checks that all the properties are copacetic.
    """
    def visit_function(self, hatchNode):
        """ Elaborate hatch node properties & recurse into child nodes to elaborate entire design.
        """
        pass
        if False:
            self.classInfoString(hatchNode)
            for c in hatchNode.childNodes:
                self.visit_function(c)   # Recurse into child Nodes.


    # TODO - Force all properties to lower case ?
    # split this into functions by property (but triggersused) ?


    # TODO - use "if properties.get('xyz', False):" instead of try-except ?

    def elaborateRegisterProperty(self, p, hatchNode):
        """ Return a string that expands all the general register modes for
            the property given.
        """
        
        try:
            value = hatchNode.properties[p]
        except KeyError, e:
            # Property does not exist, skip it.
            return "" 

        sl = []
        if 'r' in value:        # TODO - grab these from hatch_constants.py
            sl.append("readable")
        if 'w' in value:
            sl.append("writable")
        if 'i' in value:
            sl.append("incremented")
        if 'd' in value:
            sl.append("decremented")
        if 's' in value:
            sl.append("set")
        if 'c' in value:
            sl.append("cleared")
        if 'p' in value:
            str = "preset to "
            try:
                v = hatchNode.properties[p]
            except KeyError, e:
                # Property does not exist, skip it.
                str += "0"
            else:
                str += v
            sl.append(str)
   
        str = ""
        if len(sl) > 0:
            str += ", ".join(sl[:-1])
            if len(sl) > 1:
                str += " and "
            str += sl[-1]
        return str

    def elaborateSpecialRegisterProperty(self, p, hatchNode):
        """ Return a string that expandsd all the special register mode t for
            the property given.
        """
        try:
            value = hatchNode.properties[p]
        except KeyError, e:
            # Property does not exist, skip it.
            return "" 

        sl = []
        if 't' in value:
            str = "asserts " + str(hatchNode) + "_trigger for "
            try:
                v = hatchNode.properties['trigger_count']
            except KeyError, e:
                str += "one clock cycle"
            else:
                str += v + " clock cycles"
            sl.append(str)

        # Add more special properties here as they come up.

        str = ""
        if len(sl) > 0:
            str += ", ".join(sl[:-1])
            if len(sl) > 1:
                str += " and "
            str += sl[-1]
        return str


    def elaborateGeneralRegisterProperty(self, hatchNode):
        """ Return a string that expands the general register modes for a
            register.
        """

        modeSet = set()
        for m in ['sw', 'hw', 'auto']:
            try:
                value = hatchNode.properties[m]
            except KeyError, e:
            # Property does not exist, skip it.
                pass
            else:
                modeSet |= set(value)

        sl = []
        if 'e' in modeSet:
            try:
                aed = int(hatchNode.properties['auto_error_detect'])
            except ValueError, e:
                aed = 1
            except KeyError, e:
                pass    # Property does not exist, skip it.
            sl.append(str(aed) + " bit error detection")
            try:
                aec = int(hatchNode.properties['auto_error_correct'])
            except ValueError, e:
                aec = 1
            except KeyError, e:
                pass    # Property does not exist, skip it.
            sl.append(str(aec) + " bit error correction")

        # Add more general properties here as they come up.

        str = ""
        if len(sl) > 0:
            str += ", ".join(sl[:-1])
            if len(sl) > 1:
                str += " and "
            str += sl[-1]
        return str

        
        
    def classInfoString(self, hatchNode, indent=" "*4, auto=True, sw=True, hw=True, etc=True):  # TODO - fix indent issues.
        """ Modify the comment property for this hatchNode by converting it to
            a list of strings describing register access information.
        """

        # convert the existing comment.
        try:
            c = hatchNode.properties[COMMENT]
        except KeyError, e:
            # no comment yet, so add an empty list.
            hatchNode.properties[COMMENT] = []
        else:
            # convert the existing comment to a list.
            if type(c) is not list:
                hatchNode.properties[COMMENT] = [c]


        if hatchNode.has_property('external'):
            st = indent + "All functions associated with this register are implemented in external hardware "
            st = indent + "using %s_out, %s_we and %s_in." % (str(hatchNode), str(hatchNode), str(hatchNode))
            hatchNode.properties[COMMENT].append(st)
            # we are done! all other information provided in this
            # register is bogus because it cannot be verified by the
            # tool.  it's all external.
            return
        
        for m in ['sw', 'hw', 'auto']:
            try:
                value = hatchNode.properties[m]
            except KeyError, e:
            # Property does not exist, skip it.
                pass
            else:
                p = self.elaborateRegisterProperty(m, hatchNode)
                s = self.elaborateSpecialRegisterProperty(m, hatchNode)
                
                if len(p) > 0:
                    if m == 'sw':
                        p = "Is " + p + " by software"
                    elif m == 'hw':
                        p = "Is " + p + " by hardware"
                    else:
                        p = "Is " + p + " automaticaly"

                if len(p) > 0 and len(s) > 0:
                    p += " and " + s + "."
                elif len(p) > 0 and len(s) == 0:
                    p += "."
                elif len(p) == 0 and len(s) > 0:
                    if m == 'sw':
                        p = "Software " + s + "."
                    elif m == 'hw':
                        p = "Hardware " + s + "."
                    else:
                        p = "Automaticaly " + s + "."

                hatchNode.properties[COMMENT].append(p)

        # now the general stuff for this register.
        g = self.elaborateGeneralRegisterProperty(hatchNode)
        if len(g) > 0 :
            hatchNode.properties[COMMENT].append(g)


# Main for test
if __name__ == "__main__":

    consoleHandler.setLevel(logging.DEBUG)

    ie = HatchInfoElaborator()

    from hatch_nodes.hatch_node_physical import HatchNodePhysical
    h0 = HatchNodePhysical('h0', hw='rwdicsp', sw='w')
    h1 = HatchNodePhysical('h1', sw='rwt', sw_load_enable=2, auto_clear_on=0x233)
    h2 = HatchNodePhysical('h2', auto='dicspt', auto_clear_on=0x123, auto_set_on='bloo', auto_trigger_on='23', trigger_count=6)
    h3 = HatchNodePhysical('h3', comment="blabla!", input_ports='ip', output_ports='op', external=True)
    h4 = HatchNodePhysical('h4', rtl="RTL IS HERE")
    h5 = HatchNodePhysical('h5', auto_error_detect=1, auto_error_correct=1)
    h6 = HatchNodePhysical('h6', auto_error_detect=4, auto_error_correct=4)

    hatchNodes = [h0, h1, h2, h3, h4, h5, h6]

    for hatchNode in hatchNodes:
        ie.visit(hatchNode)
        print hatchNode.info(recurse=False) # Print properties.
        raw_input()


